package com.stark.wechat.service.draft;

import com.dtflys.forest.Forest;
import com.fasterxml.jackson.databind.JsonNode;
import com.stark.wechat.core.Config;
import com.stark.wechat.service.draft.model.Articles;
import com.stark.wechat.service.draft.model.Draft;
import com.stark.wechat.service.draft.model.DraftBatchgetResponsePayload;
import com.stark.wechat.util.JacksonUtils;
import com.stark.wechat.util.ResponseUtils;
import lombok.Builder;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.util.Objects.requireNonNull;

/**
 * 草稿箱接口
 *
 * @author <a href="mailto:mengbin@hotmail.com">Ben</a>
 * @version 1.0.0
 * @see <a href="https://developers.weixin.qq.com/doc/service/guide/product/draft.html">能力接入 / 草稿管理和商品卡片</a>
 * @since 2024/5/27
 */
@Builder
@Slf4j
public class DraftService {

    private final Config config;

    /**
     * 草稿箱开关设置
     * <p>本接口用于设置或查询草稿箱和发布功能的开关状态。
     * <ol>
     *     <li>内测期间会逐步放量，任何用户都可能会自动打开；</li>
     *     <li>此开关开启后不可逆，换言之，无法从开启的状态回到关闭；</li>
     *     <li>内测期间，无论开关开启与否，旧版的图文素材API，以及新版的草稿箱、发布等API均可以正常使用；</li>
     *     <li>在内测结束之后，所有用户都将自动开启，即草稿箱、发布等功能将对所有用户开放，本开关连同之前的图文素材API也将随后下线。</li>
     * </ol>
     *
     * <p><b>其他说明</b>
     * <p>由于草稿箱和发布功能仍处于内测阶段，若公众号没有被灰度覆盖，可能无法体验草稿箱和发布功能。为了解决这个问题，我们在上述API接口的基础上，设了这样一个开关：当一个公众号选择开启后，该账号在微信公众平台后台（mp.weixin.qq.com)上的图文素材库将升级为草稿箱，并可以在微信公众平台后台使用发布功能。
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param checkonly   设置开关状态时传空，仅检查状态时传1
     * @return 开关状态：0 表示开关处于关闭，1 表示开启成功（或开关已开启）
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/draftbox/draftmanage/api_draft_switch.html">草稿管理和商品卡片 / 草稿管理 / 草稿箱开关设置</a>
     */
    public Integer draftSwitch(String accessToken, Integer checkonly) {
        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/draft/switch")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .addQuery("checkonly", checkonly)
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("草稿箱开关设置", root, CODE_MAP.get("draftSwitch"));
        return root.has("is_open") ? root.get("is_open").asInt() : null;
    }

    /**
     * 更新草稿
     * <p>本接口用于修改图文或图片消息草稿。
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param mediaId     要修改的图文消息的id
     * @param index       要更新的文章在图文消息中的位置（多图文消息时，此字段才有意义），第一篇为0
     * @param articles    图文信息
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/draftbox/draftmanage/api_draft_update.html">草稿管理和商品卡片 / 草稿管理 / 更新草稿</a>
     */
    public void draftUpdate(String accessToken, String mediaId, Integer index, Articles articles) {
        requireNonNull(accessToken);
        requireNonNull(mediaId);
        requireNonNull(index);
        requireNonNull(articles);

        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/draft/update")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .addBody("media_id", mediaId)
                .addBody("index", index)
                .addBody("articles", articles)
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("更新草稿", root, CODE_MAP.get("draftUpdate"));
    }

    /**
     * 获取草稿列表
     * <p>新增草稿之后，可用本接口获取草稿列表信息。
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param offset      从全部素材的该偏移位置开始返回，0表示从第一个素材返回
     * @param count       返回素材的数量，取值在1到20之间
     * @param noContent   1 表示不返回 content 字段，0 表示正常返回，默认为 0
     * @return 草稿列表
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/draftbox/draftmanage/api_draft_batchget.html">草稿管理和商品卡片 / 草稿管理 / 获取草稿列表</a>
     */
    public DraftBatchgetResponsePayload draftBatchget(String accessToken, Integer offset, Integer count, Integer noContent) {
        requireNonNull(accessToken);
        requireNonNull(offset);
        requireNonNull(count);
        noContent = noContent != null ? noContent : 0;

        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/draft/batchget")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .addBody("offset", offset)
                .addBody("count", count)
                .addBody("no_content", noContent)
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("获取草稿列表", root, CODE_MAP.get("draftBatchget"));
        return JacksonUtils.deserialize(responseStr, DraftBatchgetResponsePayload.class);
    }

    /**
     * 新增草稿
     * <p>本接口用于修改图文或图片消息草稿。
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param articles    图文素材列表
     * @return 上传后的获取标志，长度不固定，但不会超过 128 字符
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/draftbox/draftmanage/api_draft_add.html">草稿管理和商品卡片 / 草稿管理 / 新增草稿</a>
     */
    public String draftAdd(String accessToken, List<Articles> articles) {
        requireNonNull(accessToken);
        requireNonNull(articles);

        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/draft/add")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .addBody(articles)
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("新增草稿", root, CODE_MAP.get("draftAdd"));
        return root.get("media_id").asText();
    }

    /**
     * 获取草稿的总数
     * <p>获取草稿的总数，此接口只统计数量，不返回草稿的具体内容
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @return 草稿总数
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/draftbox/draftmanage/api_draft_count.html">草稿管理和商品卡片 / 草稿管理 / 获取草稿的总数</a>
     */
    public long count(String accessToken) {
        requireNonNull(accessToken);

        String responseStr = Forest
                .get(config.getWechatGateway() + "/cgi-bin/draft/count")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("获取草稿的总数", root, CODE_MAP.get("count"));
        return root.get("total_count").asLong();
    }

    /**
     * 删除草稿
     * <p>删除指定草稿，节省空间。此操作不可撤销，请谨慎操作。
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param mediaId     要删除的草稿的media_id
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/draftbox/draftmanage/api_draft_delete.html">草稿管理和商品卡片 / 草稿管理 / 删除草稿</a>
     */
    public void draftDelete(String accessToken, String mediaId) {
        requireNonNull(accessToken);
        requireNonNull(mediaId);

        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/draft/delete")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .addBody("media_id", mediaId)
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("删除草稿", root, CODE_MAP.get("draftDelete"));
    }

    /**
     * 获取草稿详情
     * <p>新增草稿后，可通过该接口下载草稿
     *
     * @param accessToken 接口调用凭证，可使用 <a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html">access_token</a>、<a href="https://developers.weixin.qq.com/doc/oplatform/developers/dev/AuthorizerAccessToken.html">authorizer_access_token</a>
     * @param mediaId     要获取的草稿的media_id
     * @return 草稿
     * @see <a href="https://developers.weixin.qq.com/doc/service/api/draftbox/draftmanage/api_getdraft.html">草稿管理和商品卡片 / 草稿管理 / 获取草稿详情</a>
     */
    public Draft getDraft(String accessToken, String mediaId) {
        requireNonNull(accessToken);
        requireNonNull(mediaId);

        String responseStr = Forest
                .post(config.getWechatGateway() + "/cgi-bin/draft/get")
                .contentTypeJson()
                .addQuery("access_token", accessToken)
                .addBody("media_id", mediaId)
                .executeAsString();
        JsonNode root = JacksonUtils.readTree(responseStr);
        ResponseUtils.findError("获取草稿详情", root, CODE_MAP.get("getDraft"));
        return JacksonUtils.deserialize(responseStr, Draft.class);
    }

    private static final Map<String, Map<Long, String>> CODE_MAP;

    static {
        CODE_MAP = new HashMap<>();

        Map<Long, String> codeMap = new HashMap<>();
        codeMap.put(40114L, "invalid index value");
        codeMap.put(41039L, "invalid content_source_url");
        codeMap.put(45166L, "invalid content");
        codeMap.put(53404L, "账号已被限制带货能力，请删除商品后重试");
        codeMap.put(53405L, "插入商品信息有误，请检查参数及商品状态");
        codeMap.put(53406L, "请先开通带货能力");
        codeMap.put(88000L, "without comment privilege");
        CODE_MAP.put("draftUpdate", Collections.unmodifiableMap(codeMap));

        codeMap = new HashMap<>();
        codeMap.put(53404L, "账号已被限制带货能力，请删除商品后重试");
        codeMap.put(53405L, "插入商品信息有误，请检查参数及商品状态");
        codeMap.put(53406L, "请先开通带货能力");
        CODE_MAP.put("draftAdd", Collections.unmodifiableMap(codeMap));

        codeMap = new HashMap<>();
        codeMap.put(40007L, "不合法的媒体文件 id");
        CODE_MAP.put("draftDelete", Collections.unmodifiableMap(codeMap));

        codeMap = new HashMap<>();
        codeMap.put(40007L, "无效的媒体ID");
        CODE_MAP.put("getDraft", Collections.unmodifiableMap(codeMap));
    }

}
